﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace HoNSharp
{
    struct HoNServer
    {
        public Int32 ID;
        public string Host;
        public string ServerName;
        public UInt16 RemotePort;
        public string SessionID;
        public string Region;
        public string Info;
        public string Version;
        public UInt16 LookupKey;
    }

    public class HoNServerTester
    {
        Dictionary<UInt16, HoNServer> Servers;
        List<UInt16> ServerKeys;
        UdpClient udp;
        GenericBuffer gb;
        HoNRecvBuffer r;

        public HoNServerTester()
        {
            this.Servers = new Dictionary<UInt16, HoNServer>();
            this.ServerKeys = new List<UInt16>();
            this.udp = new UdpClient();
            this.udp.Client.ReceiveTimeout = 1000;
            this.gb = new GenericBuffer();
            this.r = new HoNRecvBuffer();
        }

        public void AddServer(Int32 ServerID, string Server, UInt16 Port, string Session, string Location)
        {
            HoNServer s = new HoNServer();
            s.ID = ServerID;
            s.SessionID = Session;
            s.Host = Server;
            s.RemotePort = Port;
            s.Region = Location;

            Random rnd = new Random();
            UInt16 tmp = Convert.ToUInt16(rnd.Next() % 65535);
            while (ServerKeys.Contains(tmp))
            {
                tmp = Convert.ToUInt16(rnd.Next() % 65535);
            }

            s.LookupKey = tmp;
            this.ServerKeys.Add(tmp);

            this.Servers.Add(tmp, s);
        }

        public void TestServers()
        {
            foreach (HoNServer s in this.Servers.Values)
            {
                IPEndPoint ip = new IPEndPoint(IPAddress.Parse(s.Host), Convert.ToInt32(s.RemotePort));

                gb.InsertWORD(0);
                gb.InsertBYTE(0x01);
                gb.InsertBYTE(0xCA);
                gb.InsertWORD(s.LookupKey);

                udp.Send(gb.GetBytes(), gb.GetLength(), ip);
                gb.Flush();

                try
                {
                    byte[] recvBytes = udp.Receive(ref ip);
                    r.Add(recvBytes);
                }
                catch (SocketException)
                {
                    // Ignore.
                }

                //System.Diagnostics.Debug.WriteLine("--- Game Recv ---");
                //System.Diagnostics.Debug.WriteLine(DebugOutput(r.GetBuffer()));
            }

            // Parse the stupid thingie.
            while (r.HasPacket())
            {
//0000: 00 00 01 66 39 21 55 53 53 20 34 37 00 00 00 00     ...f9!USS 47....
//0001: 30 2E 31 2E 36 36 2E 31 00 00 01 00 00 55 53 53     0.1.66.1.....USS
//0002: 00 00 6E 6F 72 6D 61 6C 00 00 00 00 00 00
                r.ExtractWORD();    // 00 00
                r.ExtractWORD();    // 01 66
                UInt16 ServerKey = r.ExtractWORD();
                string ServerName = r.ExtractString();
                r.ExtractWORD();    // 00 00
                r.ExtractByte();    // 00
                string ServerVersion = r.ExtractString();
                r.ExtractDWORD();   // 00 01 00 00
                string ServerRegion = r.ExtractString();
                r.ExtractByte();
                string ServerInfo = r.ExtractString();
                r.ExtractDWORD();   // 00 00 00 00
                r.ExtractByte();    // 00

                if (this.Servers.ContainsKey(ServerKey))
                {
                    HoNServer tmp = (HoNServer)this.Servers[ServerKey];
                    tmp.Info = ServerInfo;
                    tmp.ServerName = ServerName;
                    tmp.Version = ServerVersion;

                    System.Diagnostics.Debug.WriteLine(tmp.ServerName + ": " + tmp.Host + " " + tmp.RemotePort.ToString());
                }

                r.UpdateBuffer();
            }
        }

        // Not my function, public domain use.
        public string DebugOutput(string Data)
        {
            string rtnHex = "";
            string rtnAsc = "";
            int limit = 0;
            int bytesPerLine = 16;
            for (int i = 0; i < Data.Length; i++)
            {
                //Limit bytes per line to maxlimit
                if (limit == bytesPerLine)
                {
                    limit = 0;
                    rtnHex += "\n";
                    rtnAsc += "\n";
                }
                //Add the data to the output
                rtnHex += ((byte)Convert.ToChar(Data.Substring(i, 1))).ToString("X").PadLeft(2, '0') + " ";
                rtnAsc += ChrFormat(Convert.ToChar(Data.Substring(i, 1)));
                //Increment limit
                limit++;
            }
            //Split lines
            char[] delimiter = new char[] { '\n' };
            string[] split1 = rtnHex.Split(delimiter);
            string[] split2 = rtnAsc.Split(delimiter);
            string rtnStr = "";
            //Rebuild the returnstr
            for (int i = split1.GetLowerBound(0); i <= split1.GetUpperBound(0); i++)
            {
                //Check length of split1
                while (split1[i].Length < (3 * bytesPerLine)) { split1[i] += "   "; }
                //Check length of split2
                while (split2[i].Length < bytesPerLine) { split2[i] += "."; }
                rtnStr += i.ToString().PadLeft(4, '0') + ": " + split1[i] + "    " + split2[i] + "\n";
            }
            //Return returnstr
            return rtnStr;
        }

        private char ChrFormat(char CHR)
        {
            if ((byte)CHR < 32) { return '.'; }
            else if (((byte)CHR > 127) && ((byte)CHR < 159)) { return '.'; }
            else { return CHR; }
        }
    }
}
